
Type SpriteType Abstract
	Const sprTypeInvalid = -1
	Const sprTypeHero = 0
	Const sprTypeSmallEnemy = 1
	Const sprTypeMediumEnemy = 2
	Const sprTypeLargeEnemy = 3
	Const sprTypeWalkabout = 4
	Const sprTypeWeapon = 5
	Const sprTypeAttack = 6
	Const sprTypeBoxBorder = 7
	Const sprTypePortrait = 8
	Const sprTypeLastPT = 8
	Const sprTypeMXS = 9    'Can't change this! Saved in .slice files
	Const sprTypeLast = 9   'Last sprite type selectable in slice editor
	Const sprTypeFrame = 10  'A sprite not loaded from file, but from a Frame. Free to change this later (never saved)
End Type

Type RGBP

	Field r:Int,b:Int,g:Int
End Type

Type Palette256
	Field loaded:Byte = False 'shuld help speed up stuff
	Field col:Int[256]' as ubyte 'indices into the master palette
	Field refcount:Int' as integer 'private
End Type


Type XYPair

	Field x:Int,y:Int,w:Int,h:Int
	Field n:Int = 1

End Type

Type NPCType
	Field picture:Int' as integer     '+0
	Field palette:Int' as integer     '+1
	Field movetype:Int' as integer    '+2
	Field speed:Int' as integer       '+3  real speed, not value in .d
	Field textbox:Int' as integer     '+4
	Field facetype:Int' as integer    '+5
	Field item:Int' as integer        '+6
	Field pushtype:Int' as integer    '+7
	Field activation:Int' as integer  '+8
	Field tag1:Int' as integer        '+9   appear only if
	Field tag2:Int' as integer        '+10  appear only if 2
	Field usetag:Int' as integer      '+11
	Field script:Int' as integer      '+12
	Field scriptarg:Int' as integer   '+13
	Field vehicle:Int' as integer     '+14
	Field defaultzone:Int' as integer '+15
	Field defaultwallzone:Int' as integer '+16
	Field sprite:Frame' as frame Ptr
	Field pal:Palette256' as palette16 Ptr
	Field valdata:Int[18]
End Type


Type NPCInst
	Field sl:Int'SliceFwd
	'--stored values
	Field x:Int' as integer      'npcl+0
	Field y:Int' as integer      'npcl+300
	Field id:Int' as integer     'npcl+600    0 if unused, ID + 1 for normal NPCs, -ID - 1 for hidden NPCs
	                  '  NOTE: NPCs with invalidly high ID numbers (on incompletely loaded maps)
	                  '        are automatically hidden (and obviously unsafe to unhide)
	Field xgo:Int' as integer    'npcl+1500   warning: positive to go LEFT, negative RIGHT
	Field ygo:Int' as integer    'npcl+1800   reversed as above
	Field dir:Int' as integer    'npcl+900
	Field frame:Int' as integer  'npcl+1200   0-3: ticks in walk animation loop (2 ticks per frame)
	Field extra:Int[2]' as integer
	Field ignore_walls:Int' as integer  'ignore passmap
	Field not_obstruction:Int' as integer 'can walk through npcs+heroes and vice-versa
	Field suspend_use:Int' as integer   'not activatable 
	Field suspend_ai:Int' as integer    'ignore movetype (effectively 'stand still')
	Field curzones:Int'(VECTOR)<-- gonna look for this as integer vector  'sorted vector of zones this NPC is currently inside
End Type


Type MapeditState

	Field gmap:Int[]
	Field doors:Door[100]' as door
	Field npc_inst:NPCInst[300]'<-- g'damn yo, big enough array?
	Field npc_def:NPCType[max_npc_defs]
	Field numdoors:Int
	Field numlinks:Int
	Field numnpcs:Int
	Field link:DoorLink[200]' as doorlink	
	Field map:TileMap[]' as TileMap ' dummy empty map data, will be resized later
	Field  pass:TileMap[]
	Field  emap:TileMap[]
	Field  zmap:ZoneMap[]
	Field tilesets:Int[maplayerMax]
	Field mapname:String
	Field wide:Int
	Field high:Int
	'mapedit_loadmap st, map(), pass, emap, zmap, gmap(), visible(), doors(), link(), mapname

	Method Loadmap:Int(mapnum:Int)
	
		Local bsize:Int = RPGLump.dimbinsize(binMAP)
		DebugLog "MAIN BIN SIZE!: "+bsize
		Local g:Int[bsize]
		gmap = g
		RPGLump.LoadRecord(gmap,  Lower(GamePath+Game +".map"),(bsize),mapnum)
		If gmap[31] = 0 Then gmap[31] = 2
		'mapedit_loadmap st, map(), pass, emap, zmap, gmap(), visible(), doors(), link(), mapname
		map = LoadTilemap(RPGLump.maplumpname(mapnum,"t"))
		If map = Null Then 
			Return -1
		EndIf
		pass = LoadTilemap(RPGLump.maplumpname(mapnum,"p"))
		emap = LoadTilemap(RPGLump.maplumpname(mapnum,"e"))
		mapname = getmapname(mapnum)
		wide = map[0].wide
		high = map[0].wide
		DebugLog "Name: "+mapname
		loadmaptilesets()
		DeSerDoors(GamePath+Game+".dox",doors,mapnum)
		DeserDoorLinks(RPGLump.maplumpname(mapnum,"d"), link)
		'now for some NPCs
		LoadNPCL(RPGLump.maplumpname(mapnum,"l"))
		LoadNPCD(RPGLump.maplumpname(mapnum,"n"),max_npc_defs)
		
		
		
		Rem
		SUB mapedit_loadmap (st as MapEditState, map() as TileMap, pass as TileMap, emap as TileMap, zmap as ZoneMap, gmap() as integer, visible() as integer, doors() as Door, link() as DoorLink, mapname as string)
		 loadrecord gmap(), game & ".map", getbinsize(binMAP) \ 2, st.mapnum
		 IF gmap(31) = 0 THEN gmap(31) = 2
		 visible(maplayerMax \ 16) = -1   'default all layers to visible, if they're enabled too, of course
		 loadtilemaps map(), maplumpname(st.mapnum, "t")
		 loadtilemap pass, maplumpname(st.mapnum, "p")
		 loadtilemap emap, maplumpname(st.mapnum, "e")
		 IF isfile(maplumpname(st.mapnum, "z")) THEN
		  LoadZoneMap zmap, maplumpname(st.mapnum, "z")
		 ELSE
		  CleanZoneMap zmap, map(0).wide, map(0).high
		 END IF
		 mapedit_load_tilesets st, map(), gmap()
		 LoadNPCL maplumpname(st.mapnum, "l"), st.npc_inst()
		 LoadNPCD_fixedlen maplumpname(st.mapnum, "n"), st.npc_def(), st.num_npc_defs
		 deserdoors game & ".dox", doors(), st.mapnum
		 deserdoorlinks maplumpname(st.mapnum, "d"), link()
		 mapname = getmapname(st.mapnum)
		 st.wide = map(0).wide
		 st.high = map(0).high
		 verify_map_size st, map(), pass, emap, zmap, mapname
		END SUB
	End Rem
		Return 1
	End Method
	
	Method LoadNPCD(file:String,arraylen:Int,resize:Int=0)
		Local i:Int, j:Int
		Local f:TStream
		Local  dat:NPCType[max_npc_defs]
		'It's still up to you to actually load the new sprites, but "do not pass
		'arrays holding frame pointers" would be a tricky catch
		For i = 0 To dat.Length-1
			dat[i] = New NPCType
		'	WITH dat(i)
		'	If .sprite Then frame_unload @.sprite
		'	If .pal Then palette16_unload @.pal
		'	End WITH
		Next
		
		'f = FREEFILE
		f = ReadFile(file)'OPEN file For BINARY ACCESS READ as #f
		SeekStream(f,7)'SEEK #f, 8
		
		arraylen = ((StreamSize(f)) /RPGLump.getbinsize(binN))
		DebugLog "ArrayLen: "+arraylen
		'If resize Then 
			'dat(arraylen - 1)
		
		Local recordlen:Int = (RPGLump.getbinsize(binN) /2)
		DebugLog "RecordLen: "+recordlen
		Local buf:Int[recordlen]
		
		For i = 0 To arraylen - 1
			RPGLump.loadrecords(buf, f, recordlen)
			For j = 0 To recordlen - 1
				SetNPCD(dat[i], j, buf[j])
				'DebugLog "NPCD: i: "+i+" j: "+buf[j]
			Next
			If dat[i].speed = 3 Then dat[i].speed = 10
			
		Next
		npc_def = dat
		CloseFile(f)
	End Method

	Function SetNPCD(npcd:NPCType,offset:Int,value:Int)
		 ' STATIC maxoffset as integer = -1
		 ' If maxoffset = -1 Then maxoffset = (curbinsize(binN) \ 2) - 1
		
		  If offset >= 0 And offset <= npcd.valdata.Length-1 Then
		   	'writing values directly to memory? dayum.. F that shiz.
			npcd.valdata[offset] = value
			'DebugLog "Wrote: "+offset+" value: "+value
		  Else
		  	DebugLog "Attempt to write NPC data out-of-range. offset=" + offset + " value=" + value
		  End If
	End Function
	
	'Legacy (used for .L); not kept up to date with changes to NPCInst
	Method LoadNPCL(file:String)
		Local i:Int' as integer
		Local  f:TStream' as integer
		'f = FREEFILE
		numnpcs  = 0
		f = ReadFile(file)'OPEN file For BINARY ACCESS READ as #f
		SeekStream(f,7)'SEEK #f, 8
		Local dat:NPCInst[300]'CleanNPCL dat()
		For i = 0 To 299
			If dat[i] = Null Then dat[i] = New NPCInst
			dat[i].x = ReadShort(f)*20',-1) * 20 
		Next
		For i = 0 To 299
			dat[i].y = ReadShort(f)*20',-1) - 1) * 20
		Next
		For i = 0 To 299
			dat[i].id = ReadShort(f)',-1)
		Next
		For i = 0 To 299
			dat[i].dir = ReadShort(f)',-1)
		Next
		For i = 0 To 299
			dat[i].frame = ReadShort(f)',-1)
		Next
		For i = 0 To 299
			'check if all values = 0, if so then npcs don't exist.. no point in wasting good memory on ghosts
			If dat[i].frame = 0 And dat[i].x = 0 And dat[i].y = 0 And dat[i].id = 0 And dat[i].dir = 0 Then
				Exit
			Else
				numnpcs = numnpcs + 1
			EndIf
			'DebugLog "Read-  Frame: "+dat[i].frame+" x: "+dat[i].x+" y: "+dat[i].y+" id: "+dat[i].id+" dir: "+dat[i].dir
		Next
		npc_inst = dat
		CloseFile(f)
	End Method
	
	Method DeSerDoors:Int(filename:String,array:Door[],record:Int)
		If Not FileType(filename) = 1 Then Return -1
		'Local array:Door[]
		
		Local f:TStream '= freefile, i
		Local i:Int
		
		f = ReadFile(filename)'open filename For binary access read as #f
		
		SeekStream(f, record * 600)
		
		For i = 0 To 99
			If array[i] = Null Then array[i] = New Door
			array[i].x = ReadShort(f)
		Next
		For i = 0 To 99
			array[i].y = ReadShort(f)
		Next
		For i = 0 To 99
			array[i].bits[0] = ReadShort(f)
			If array[i].bits[0] = 1 Then
				numdoors=numdoors+1
			EndIf
		Next
		
		CloseFile(f)
	End Method	

	Method DeserDoorLinks:Int(filename:String, array:DoorLink[])
		Local hasheader:Int = -1
		Local f:TStream
		Local i:Int
		'when we strip the header, we can check for its presence here
	
		If Not FileType(filename) = 1 Then
			DebugLog "couldn't load " + filename
			Return -1
		End If
		
		'f = freefile
		f = ReadFile(filename)'open filename For binary access read as #f
		
		
		'If hasheader <> -1 Then 
			'wat
			Local stupid:Byte[7]
			'how long is teh stoopid
			For i = 0 To stupid.Length-1
				stupid[i] = ReadByte(f)
			Next
			'get #f,, stupid()
		'End If
			
		For i = 0 To 199
			If array[i] = Null Then array[i] = New DoorLink
			array[i].source = ReadShort(f)
			If array[i].source <> 65535 Then
				numlinks = numlinks + 1
			EndIf
		Next
		For i = 0 To 199
			array[i].dest = ReadShort(f)
		Next
		For i = 0 To 199
			array[i].dest_map = ReadShort(f)
		Next
		For i = 0 To 199
			array[i].tag1 = ReadShort(f)
		Next
		For i = 0 To 199
			array[i].tag2 = ReadShort(f)
		Next
		
		CloseFile(f)
	End Method
	
	Rem
	Method mapedit_load_tilesets()
		loadmaptilesets st.tilesets(), gmap()
		v_new st.defaultwalls, UBOUND(map) + 1
		For i as integer = 0 To UBOUND(map)
		loadpasdefaults st.defaultwalls[i], st.tilesets(i)->num
		Next
	End Method
	End Rem
	Function layer_tileset_index:Int(layer:Int)
		'return the gmap() index containing a layer's tileset
		If layer <= 2 Then 
			Return 22 + layer 
		Else 
			Return 23 + layer
		EndIf
	End Function

	Method loadmaptilesets()
		'tilesets() may contain already loaded tilesets. In this case, we can reuse them
		Local i:Int, j:Int
		Local tileset:Int' as integer
	
		For i  = 0 To tilesets.Length-1
			tileset = gmap[layer_tileset_index(i)]
			If tileset <> 0 Then
				tileset = tileset - 1
			Else
				tileset = gmap[0]
			End If
			tilesets[i] = tileset
			DebugLog "Read Tileset: "+tileset

			'tileset are preloaded, only need the Tilset ID
			'loadtilesetdata tilesets(), i, tileset
			Rem
			If resetanimations Then
				For j as integer = 0 To 1
					WITH tilesets(i)->anim(j)
					.cycle = 0
					.pt = 0
					.skip = 0
					End WITH
				Next
			End If
			End Rem
		Next
			For Local a:Int = 0 To gmap.Length-1
				DebugLog "GMAP["+a+"] = "+gmap[a]
				
			Next		
	End Method
	Rem
	Function loadtilesetdata (tilesets:TilesetData[] ,layer:Int, tilesetnum:Int, byval lockstep as integer = YES)
	'the tileset may already be loaded
	'note that tile animation data is NOT reset if the old tileset was the same	
	
	 If tilesets(layer) = Null ORELSE tilesets(layer)->num <> tilesetnum Then
	  unloadtilesetdata tilesets(layer)
	  tilesets(layer) = New TilesetData
	
	  WITH *tilesets(layer)
	   .num = tilesetnum
	   'debug "loading tileset " & tilesetnum
	
	   .spr = tileset_load(tilesetnum)
	   loadtanim tilesetnum, .tastuf()
	  End WITH
	 End If
	 For i as integer = 0 To 1
	  WITH tilesets(layer)->anim(i)
	   .cycle = 0
	   .pt = 0
	   .skip = 0
	  End WITH
	 Next
	 If lockstep Then lockstep_tile_animation tilesets(), layer
	End Function
	End Rem
	Function getmapname:String(m:Int)
		Local nameread:Int[39]' as integer
		RPGLump.LoadRecord(nameread,GamePath+Game +".mn", 40, m)
		'I don't even...
		Local a:Int[RPGLump.Small((nameread[0] & 255), 39)]
		'Local a:Int[39]
		'For Local i:Int = 0 To nameread.Length-1
			'a = a + Chr(nameread[i])
			Local name:String = RPGLump.array2str(nameread, 1, a)
		'Next
		Return name
	End Function


	'FIXME: early versions always saved 32000 bytes of tile data (ie, 32011 total)!
	'This will cause extra spurious map layers to be loaded!
	Function LoadTilemap:Tilemap[](filename:String)
		Local fh:TStream
		Local numlayers:Int, i:Int, wide:Int, high:Int
		'For i = 0 To layers.Length-1
		'	If layers(i).data Then layers(i).data = Null
		'Next
		
		'fh = FREEFILE
		DebugLog "Reading: "+filename
		fh = ReadFile(Lower(filename))
		If fh = Null Then
			Return Null
		EndIf
		'OPEN filename For BINARY ACCESS READ as #fh
		SeekStream(fh,7)
		Local w:Short= ReadShort(fh)
		wide = RPGLump.boundInt(w, 16, 32678)
		'SeekStream(fh,10)
		Local h:Short = ReadShort(fh)
		high = RPGLump.boundInt(h, 10, 32678)
		DebugLog "Read Size W: "+w+" H: "+h
		numlayers = (StreamSize(fh) - 11) / (wide * high)
		If numlayers > maplayerMax + 1 Or numlayers * wide * high + 11 <> StreamSize(fh) Then
			'Because of bug 829 (.T not truncated when map resized), tilemaps with bad lengths are common; better not to spam this message
			'debug "tilemap " & filename & " (" & wide & "x" & high & ") bad length or size; " & LOF(fh) & " bytes"
			'show the user their garbled mess, always interesting
			numlayers = RPGLump.boundInt(numlayers, 1, maplayerMax + 1)
		End If
		Local layers:TileMap[numlayers]
		
		'REDIM layers(numlayers - 1)
		SeekStream(fh, 11)
		For i = 0 To numlayers - 1
			'WITH layers(i)
			layers[i] = New TileMap
			Local d:Int[wide * high]
			layers[i].data = d'CreateBank(wide * high)'ALLOCATE(wide * high)
			layers[i].wide = wide
			layers[i].high = high
			layers[i].layernum = i
			For Local m:Int = 0 To (wide * high)-1
				layers[i].data[m] = ReadByte(fh)
			Next
			'ReadBank( layers[i].data,fh,0,(wide * high) ) 'GET #fh, , *.data, wide * high
			'SaveBank(layers[i].data,"Map_"+i+"_"+ExtractDir(ExtractExt(filename))+".data")
			'End WITH
		Next
		CloseFile(fh)
		Return layers
	End Function

End Type

Type TileMap
	Field wide:Int' as integer
	Field high:Int' as integer
	Field data:Int[]' as ubyte Ptr
	Field layernum:Int' as integer
End Type

Type ZoneMap
	Field numzones:Int' as integer
	Field zones:ZoneInfo' Ptr
	Field wide:Int' as integer  'width in tiles
	Field high:Int' as integer  'height in tiles
	Field wide_segments:Int' as integer  'width in 4x4 tile segments
	Field high_segments:Int' as integer  'height in 4x4 tile segments
	Field bitmap:TImage'i'm gonna stick this image type here... as ushort Ptr      'array dimensions are [high][wide]
	Field zoneIDmap:Short' as ushort Ptr   'array dimensions are [high_segments][wide_segments][16]
	'Field extraID_hash:HashTable 'table of ZoneHashedSegments for overcrowded tiles, indexed by (x SHL 16) + y
End Type

Type ZoneInfo
	Field id:Int' as integer
	Field name:String
	Field hidden:Int' as integer  'used in the map editor only, not saved
	Field numtiles:Int' as integer  'number of set tiles
	Field extra:Int[2]' as integer
End Type



Type Door
	Field x:Int, y:Int
	Field bits:Int[1]'<--- why array of one??
End Type

Type DoorLink
	Field source:Int, dest:Int, dest_map:Int, tag1:Int, tag2:Int
End Type


Type MouseInfo
	Field x:Int,y:Int,clicks:Int,buttons:Int
	Field wheel:Int,dragging:Int
	Field clickstart:XYPair
End Type

Type SpriteState
	Field sets:SpriteSet[]' Ptr
	Field curframe:Frame' Ptr  'convenience ptr to set->frames[.frame_id]
	'gonna change this to not use palettes
	Field pal:Palette256' Ptr
	Field frame_id:Int' as integer
	Field numsets:Int
	'Field anim:Animation' Ptr
	Field anim_step:Int' as integer
	Field anim_wait:Int' as integer
	Field anim_loop:Int' as integer '-1:infinite, 0<:number of times to play after current
	Field offset:XYPair = New XYPair
	Field zone:XYPair = New XYPair
	Field previewpos:XYPair = New XYPair
	Field hold:Int
	Field x:Int,y:Int
	Field wide:Int,high:Int
	Field perset:Int
	Field size:Int
	Field setsize:Int
	Field zoom:Int
	Field at_a_time:Int
	Field fileset:Int
	Field spritefile:String
	Field framenum:Int
	Field fullset:Int
	Field didscroll:Int
	Field drawcursor:Int
	Field tool:Int
	Field airsize:Int
	Field mist:Int
	Field palindex:Int
	Field pt:Int
	Field top:Int
	'Field sets:Int
	Field placer:Int[]
	Field workpal:Int[]
	Field soff:Int
	Field poffset:Int[]
	
	Method CreateState(xw:Int,yw:Int,asets:Int,aperset:Int,asoff:Int,azoom:Int,ufileset:Int,ufullset:Int=0,cursor_start:Int=0,cursor_top:Int=0)
		
		Local s:SpriteSet[asets]
		sets = s
		fileset = ufileset
		spritefile = Lower(GamePath+Game + ".pt" + fileset)
		framenum = 0
		wide = xw
		high = yw
		perset = aperset
		size = wide * high / 2
		setsize = size * perset
		zoom = azoom
		at_a_time = Int(200 / (high + 5)) - 1
		fullset = ufullset
		 '--Editor
		x = 0
		y = 0
		zone.x = 0
		zone.y = 0
		hold = 0
		soff = asoff
		'gotmouse = havemouse()
		didscroll = 0
		drawcursor = 1
		tool = 0'draw_tool
		airsize = 5
		mist = 4
		palindex = 1
		previewpos.x = 319 - wide
		previewpos.y = 119	
		pt = cursor_start
		top = cursor_top
		'sets = asets
		Local pl:Int[2 + (wide * high * perset)]
		placer = pl
		Local wp:Int[8 * (at_a_time + 1)]
		workpal = wp
		Local po:Int[asets+1]
		poffset = po
		 loaddefaultpals(fileset, poffset,asets)
		' .size = ss.at_a_time	
		spriteedit_load_all_you_see()
	End Method
	
	Method spriteedit_load_all_you_see()
	 For Local j:Int = 0 To sets.length-2'top + at_a_time
		DebugLog "Loading sprites: "+j
	 	Local checkfile:Int = spriteedit_load_what_you_see(j)
		If checkfile = -1 Then 
			Return
		EndIf
	 Next
	End Method
	
	'this name is gawd aweful
	Method spriteedit_load_what_you_see:Int(j:Int)
		Local i:Int' as integer
		Local pixels:TPixmap
		'getpal16 workpal(), j - top, poffset(j)
		If j <= sets.length-1 Then
			'setpicstuf buffer(), ss.setsize, 2
			DebugLog "Loading set: "+spritefile
			'frame_load
			'pixels = loadset(spritefile, j, 0,setsize)
			sets[j] = New SpriteSet
			sets[j].InitEmpty(perset)
			
			
			
			'set.Init(perset,SpriteType.sprTypeWalkabout)
			
			For i = 0 To (perset - 1)
				sets[j].defpal = poffset[j]
				sets[j].frames[i] = sets[j].frame_load(spritefile,j,i,perset,wide,high,poffset[j] )
				numsets = j '<-------------this is all goofy, fix that
				If sets[j].frames[i] = Null Then
					
					DebugLog "Numsets: "+numsets
					Return -1
				EndIf
				'SavePixmapPNG(sets[j].frames[i].sprite,"SET_"+j+"_FRAME_"+i+".png")
			'	DebugLog "Loading pixels for set: "+i
			'	placer = set.LoadSprites(placer,pixels,0,size * i,0,wide,high)
			'	set.FrameFromBuffer(placer,0)
			'	loadsprite placer(), 0, ss.size * i, 0, ss.wide, ss.high, 2
			'	stosprite placer(), 0, ss.size * i, soff * (j - top), 3
			Next
		End If
		Return 0
	End Method

	
End Type



Type Frame
	Field w:Int' as integer
	Field h:Int' as integer
	Field pitch:Int' as integer     'pixel (x,y) is at .image[.x + .pitch * .y]; mask and image pitch are the same!
	'going to use blitzmax built in GFX API for image types instead of a byte ptr
	Field sprite:TPixmap 'for editing
	Field image:TImage' as ubyte Ptr
	Field mask:Int[3]' r,g,b mask color ////as ubyte Ptr
	Field palmap:Int[,]
	Field refcount:Int' as integer  'see frame_unload in particular for documentation
	Field arraylen' as integer  'how many frames were contiguously allocated in this frame array
	'image types in bmax carry their own frame variable so this may not be needed
	
	Field base:TImage' as Frame Ptr    'the Frame which actually owns this memory
	'Field cacheentry:SpriteCacheEntryFwd' we don't need a pointer to reference an exsting type Ptr
	Field cached:Int = 1' as integer  '(not set for views onto cached sprites)
	Field arrayelem:Int = 1' as integer  'not the first frame in a frame array
	Field isview:Int = 1' as integer

	'used only by frames in a SpriteSet, for now
	Field offset:XYPair
	'Field sprset:SpriteSetFwd' Ptr  'if not NULL, this Frame array is part of a SpriteSet which
      
	Method initframe_bysize(ww:Int,hh:Int)
		w = ww
		h = hh
		sprite = CreatePixmap( w,h,PF_RGBA8888)'64,96,PF_RGBA8888)
		Local pm:Int[w,h]'Int[64,96]
		palmap = pm	
		ClearPixels( sprite,ARGB(0,0,0,0) )
		
	End Method
	Method initframe(sset:Int)
	
		Select sset
		
			Case SpriteType.sprTypeWalkabout
			
				sprite = CreatePixmap( 20,20,PF_RGBA8888)'64,96,PF_RGBA8888)
				Local pm:Int[20,20]'Int[64,96]
				palmap = pm
				
			Case SpriteType.sprTypeHero
			
				sprite = CreatePixmap( 80,100,PF_RGBA8888 )
				Local ph:Int[80,100]
				palmap = ph	
						
			Case SpriteType.sprTypeSmallEnemy
			
				sprite = CreatePixmap( 85,85,PF_RGBA8888 )
				Local pe:Int[85,85]
				palmap = pe	
						
			Case SpriteType.sprTypeMediumEnemy
	
				sprite = CreatePixmap( 125,125,PF_RGBA8888 )
				Local pem:Int[125,125]
				palmap = pem		
				
		End Select
		'should clear this with palette color 0
		ClearPixels( sprite,ARGB(0,0,0,0) )
		
	End Method
	

End Type



Type SpriteSet
	Field numanimations:Int' as integer
	'Field animations:Animation' Ptr
	Field numframes:Int' as integer  'redundant to frames->arraylen
	Field frames:Frame[]' Ptr
	Field defpal:Int
	'uses refcount from frames
	Method Init(numf:Int,sset:Int)
		
		numframes = numf
		Local f:Frame[numf]
		For Local i:Int = 0 To numf-1
		
			f[i] = New Frame
			f[i].initframe(sset)
		
		Next
		frames = f
		
		
	End Method
	
	Method InitEmpty(numf:Int)
		
		numframes = numf
		Local f:Frame[numf]
		'For Local i:Int = 0 To numf-1
		
			'f[i] = New Frame
			'f[i].initframe(sset)
		
		'Next
		frames = f
		
		
	End Method

	Function frame_load:Frame(file:String,rec:Int,num:Int,setnum:Int,wide:Int,height:Int,po:Int)
		Local ret:frame = New Frame' as frame Ptr
	
		'first, we do a bit of math:
		Local frsize:Int = (wide * height)/2
		Local recsize:Int = (frsize*setnum)*rec'frsize * (num+1)
	
		'make sure the file is real
		If Not FileType(file) = 1 Then
			DebugLog "frame_load: can't read " + file
			Return Null
		End If
	
		'now, we can load the sprite
		Local f:TStream' as integer = freefile
	
		'open() returns 0 for success
		f = ReadFile(file)
		If f = Null Then'(fi For binary access read as #f) Then
			DebugLog "sprites: could not open " + file
			Return Null
		End If
	
		'if we get here, we can assume that all's well, and allocate the memory
		ret.initframe_bysize(wide,height)' = frame_new(wid, hei, num)
	
		'If ret = 0 Then
		'	close #f
		'	Return 0
		'End If
	
		'find the right sprite (remember, it's base-1)
		Local seek:Int
		'DebugLog "Seek: "+(recsize+((frsize)*num))
		If setnum = 0 Then
			seek = SeekStream( f,(recsize+((frsize)*num)))
		Else
			seek = SeekStream( f,(recsize+((frsize)*num)))
		EndIf
		'seek #f, recsize * rec + 1
		If seek = -1 Then
			DebugLog "End of Line"
			CloseFile(f)
			Return Null
		Else If Eof(f)	
			DebugLog "End of Line"
			CloseFile(f)
			Return Null			
		EndIf
		Local i:Int,x:Int,y:Int,z:Byte' as integer, x as integer, y as integer, z as ubyte
	
		'For i = 0 To num
			'with ret[i]
				'although it's a four-bit sprite, it IS an 8-bit bitmap.
	
				For x = 0 To wide - 1
					For y = 0 To height - 1
						'pull up two pixels
						If Eof(f) Then 
							CloseFile(f)
							Return Null
						EndIf
						z = ReadByte(f)
						'get #f,,z
						'DebugLog "Writing to X: "+x+" y: "+y
						'the high nybble is the first pixel
						'.image[y * wid + x] = (z Shr 4)
						ret.palmap[x,y] = (z Shr 4)
						ret.sprite.WritePixel( x,y,DefPalette[po].col[ret.palmap[x,y]] )
						y = y+1
						'DebugLog "Writing to X: "+x+" y: "+y
						'and the low nybble is the second one
						'.image[y * wid + x] = z And 15
						ret.palmap[x,y] = z & 15
						ret.sprite.WritePixel( x,y,DefPalette[po].col[ret.palmap[x,y]] )
	
						'it is worth mentioning that sprites are stored in columns, not rows
						'UR WEIRD BRO..
					Next
				Next
			'End with
		'Next
	
		CloseFile(f)
		'SavePixmapPNG( ret.sprite,"Test_"+Rand(1,100)+".png" )
		Return ret
	End Function


	Method FrameFromBuffer:Frame(pic:Int[],picoff:Int)
		Local sw:Int' as integer
		Local sh:Int' as integer
		Local hspr:Frame' as frame Ptr
		Local dspr:Int' as ubyte Ptr
		Local nib:Int' as integer
		Local i:Int' as integer
		Local spix:Int' as integer
		Local row:Int' as integer	
		
		sw = pic[picoff]
		sh = pic[picoff+1]
		DebugLog "W: "+sw+" H: "+sh
		picoff = picoff + 2	
		hspr = New Frame
		hspr.w = sw
		hspr.h = sh
		hspr.sprite = CreatePixmap( hspr.w,hspr.h,PF_RGB888)'64,96,PF_RGBA8888)
		Local pm:Int[hspr.w,hspr.h]'Int[64,96]
		hspr.palmap = pm	
		'now do the pixels
		'pixels are in columns, so this might not be the best way to do it
		'maybe just drawing straight to the screen would be easier
		'**wtf is this? the last two loading functions they were in rows, now suddenly a wild columns appear??
		nib = 0
		row = 0
		For i = 0 To (sw * sh) - 1
			Select nib			' 2 bytes = 4 nibbles in each int
				Case 0
					spix = (pic[picoff] & $f000) Shr 12
				Case 1
					spix = (pic[picoff] & $0f00) Shr 8
				Case 2
					spix = (pic[picoff] & $f0) Shr 4
				Case 3
					spix = pic[picoff] & $0f
					
			End Select
			'HOW THE HELL DOES THIS WORK?!
			'DebugLog "WritePixel: x: "+dspr+" y: "+row+" rgb: "+spix
			
			hspr.sprite.WritePixel( row,dspr,DefPalette[0].col[spix] )
			picoff = picoff + 1
			
			'*dspr = spix				' set image pixel
			'dspr = dspr + sw  'does this wrap to the next pixel?
			row = row + 1
			If (row >= sh) Then	'ugh
				'dspr = dspr - (sw * sh) 'this resets back to beginning?
				dspr = dspr + 1 'then it goes to the next pixel??
				row = 0
			End If
			nib = nib + 1
			nib = nib & 3
		Next
		SavePixmapPNG( hspr.sprite,"Test_"+Rand(1,100)+".png" )
		Return hspr		
			
		
	
	End Method	

	
	Method LoadSprites:Int[](pic:Int[],pixels:TPixmap,picoff:Int,x:Int,y:Int,w:Int,h:Int)
		'(pic(),picoff,x,y ,w,h, byval page as integer)
		Local i:Int
		Local poff:Int' as integer
		Local toggle:Int' as integer
		Local sbytes:Int' as integer
		Local temp:Int' as integer		
		'Local px:TPixmap = CreatePixmap( 320,200,PF_RGB888)
		sbytes = ((w * h) + 1) / 2
		'Local readoff:Int = (x/w)*(w*h)
		y = y +( x / 320)
		x = x Mod 320
		DebugLog "Sprite start x "+x+" y "+y
		toggle = 0
		poff = picoff
		pic[poff] = w			'these are 4byte ints, not compat w. orig.
		pic[poff+1] = h
		poff = poff + 2
		DebugLog "Sbytes: "+sbytes
		For i = 0 To sbytes - 1
			'Local place:Int = 
			'DebugLog "Read "+((readoff)+i)
			'temp = pixels[i]
			temp = ReadPixel(pixels,x,y)
			If toggle = 0 Then
				pic[poff] = temp Shl 8
				toggle = 1
			Else
				pic[poff] = pic[poff] | temp
				'DebugLog "Pixel: "+pic[poff]
				poff = poff + 1
				toggle = 0
			End If
			'px.WritePixel(x,y,pixels[i])
			'frames[poff].sprite.WritePixel(x,y,pixels[i])
			'toggle=~1
			x = x + 1
			If x = 320 Then
				y = y + 1
				x = 0
			End If
			'DebugLog "Pic offset: "+poff+" x: "+x+" y:"+y
		Next	
		Return pic
		'SavePixmapPNG( px,Rand(1,100)+"poop.png" )
	End Method
	
End Type


Type TileAnimState
	Field cycle:Int' AS INTEGER
	Field pt:Int' AS INTEGER
	Field skip:Int' AS INTEGER
End Type

Type TilesetData
	Field tilesetfile:String 
	Field num:Int' as integer
	Field spr:Frame' as Frame Ptr
	'array of one??
	Field anim:TileAnimState[1]' as 
	Field tastuf:Int[40]' as integer
	Field wide:Int = 20
	Field high:Int = 20
	
	Method LoadTileset(offset:Int=0)
		tilesetfile = Lower(GamePath+Game + ".til" )
		spr = loadmxs:Frame (tilesetfile,offset) 
		If spr = Null Then 
			spr = New Frame
			spr.initframe_bysize(320,200)
			Return False
		Else
			Return True
		EndIf
	End Method


	
End Type

Type TilsetState

	Field Tileset:TilesetData[Gen[genMaxTile]+1]
	Field numsets:Int 

	
	Method Loadsets()
		For Local i:Int = 0 To Gen[genMaxTile]
			Local td:TilesetData = New TilesetData
			Local loaded:Byte = td.LoadTileset(i)
			'If loaded = True Then
				Tileset[i]  = td
				numsets = numsets+1
				
			'EndIf
			
		Next
	End Method

End Type


Type SoundState

	Field Ssounds:TSFX[]
	Field numsets:Int
	
	Method CreateState()
	
		Local s:TSFX[Gen[genMaxSFX]]
	
		For Local i:Int = 0 To Gen[genMaxSFX]-1
		
			s[i] = New TSFX
			' i think the sounds are 0 based
			Local valid:Int = s[i].LoadSoundFX(i)
			
			If valid = 1 Then
				numsets = numsets + 1
			Else
				Ssounds = s
				Return
				
			EndIf
			
		
		
		Next
		Ssounds = s
	
	End Method


End Type


Type TSFX
	Field soundfile:String
	Field soundnum:Int
	Field soundname:String
	
	Method LoadSoundFX:Int(id:Int)
		'i cannot find the song loading functions so i am just going to pull in the songs that exists when unlumped.
		Local soundbase:String
		soundnum = id
		
		soundbase = GamePath + "sfx" + soundnum
		soundfile = ""
		
		If FileType(soundbase + ".mp3") = 1 Then
			soundfile = soundbase + ".mp3"
		ElseIf FileType(soundbase + ".ogg")= 1 Then
			soundfile = soundbase + ".ogg"
		ElseIf FileType(soundbase + ".wav")= 1 Then
			soundfile = soundbase + ".wav"
		End If
		
		If soundfile = "" Then 
			Return -1
		Else
			DebugLog "Loaded Sound: "+soundfile
			Return 1
		EndIf
		'loadsong songfile		
		
		Return -1
	
	End Method

	
End Type



Type TSong
	Field songfile:String
	Field songnum:Int
	Field songname:String
	
	Method LoadSong:Int(id:Int)
		'i cannot find the song loading functions so i am just going to pull in the songs that exists when unlumped.
		Local songbase:String
		songnum = id
		
		songbase = GamePath + "song" + songnum
		songfile = ""
		
		If FileType(songbase + ".mp3") = 1 Then
			songfile = songbase + ".mp3"
		ElseIf FileType(songbase + ".ogg")= 1 Then
			songfile = songbase + ".ogg"
		ElseIf FileType(songbase + ".mod")= 1 Then
			songfile = songbase + ".mod"
		ElseIf FileType(songbase + ".xm")= 1 Then
			songfile = songbase + ".xm"
		ElseIf FileType(songbase + ".s3m")= 1 Then
			songfile = songbase + ".s3m"
		ElseIf FileType(songbase + ".it")= 1 Then
			songfile = songbase + ".it"
		ElseIf FileType(songbase + ".mid")= 1 Then
		Rem
			Local fileconvert:String = AppDir+"/bin/timidity.exe "+songbase + ".mid -Ow -o | ffmpeg -i - -acodec libmp3lame -ab 256k "+songbase +".mp3"'| ffmpeg -i - -acodec libmp3lame -ab 256k out.mp3
			'system_(fileconvert)
			RunApp(fileconvert)
			Repeat
				If Checkstatus() = -1 Then Exit
			Forever
		End Rem
			songfile = songbase + ".mid"
		ElseIf FileType(songbase + ".bam")= 1 Then
			songfile = songbase + ".bam"
		ElseIf FileType(GamePath + Game + "." + songnum)= 1 Then
			songfile = GamePath+Game + "." + songnum ' old-style BAM naming scheme
		End If
		
		If songfile = "" Then 
			Return -1
		Else
			DebugLog "Loaded Song: "+songfile
			Return 1
		EndIf
		'loadsong songfile		
		
		Return -1
	
	End Method
	
	Method getsongname:String(prefixnum:Int=0)
		Local num = songnum
		Local songd:Int[RPGLump.dimbinsize(binSONGDATA)]' as integer
		Local s:String' as String
		If num = -1 Then Return "-none-"
		s = ""
		If prefixnum > 0 Then s = num + " "
			'WTF this is music not an image.. how the ?!
			
			'setpicstuf songd(), curbinsize(binSONGDATA), -1
			'loadset workingdir + SLASH + "songdata.bin", num, 0
			's = s & readbinstring(songd(), 0, 30)
		songname = s
	End Method	
	
End Type


Type SongState

	Field Ssongs:TSong[]
	Field numsets:Int
	
	Method CreateState()
	
		Local s:TSong[Gen[genMaxSong]]
	
		For Local i:Int = 0 To Gen[genMaxSong]-1
		
			s[i] = New TSong
			' i think the songs are 1 based /headdesk
			Local valid:Int = s[i].LoadSong(i+1)
			
			If valid = 1 Then
				numsets = numsets + 1
			Else
				Ssongs = s
				Return
				
			EndIf
			
		
		
		Next
		Ssongs = s
	
	End Method


End Type


Type BackdropState

	Field Backdrop:BackdropData[Gen[genNumBackdrops]]
	Field numsets:Int 

	
	Method Loadsets()
		For Local i:Int = 0 To Gen[genNumBackdrops]-1
			Local bd:BackdropData = New BackdropData
			Local loaded:Byte = bd.LoadBackdrop(i)
			If loaded = True Then
				Backdrop[i]  =bd
				numsets = numsets+1
			Else
				Return
			EndIf
			
		Next
	End Method

End Type


Type BackdropData

	Field backdropfile:String 
	Field num:Int' as integer
	Field spr:Frame' as Frame Ptr
	
	Method LoadBackdrop(offset:Int=0)
		backdropfile = Lower(GamePath+Game +".mxs" )
		spr = loadmxs:Frame (backdropfile,offset) 
		If spr = Null Then 
			spr = New Frame
			spr.initframe_bysize(320,200)
			Return False
		Else
			Return True
		EndIf
	End Method


End Type